------------------------------
-- Author: Simca@Malfurion  --
------------------------------

--[[
CHANGELOG v1.3.0:
1. Added Pet Battle Teams support via a function submitted by durandal42. Thanks!
2. Added options for the new Pet Battle Teams support. You must have either auto-equip active for Wild Pets or Pet Tamers (or both) to use the feature.
3. Significant cleanup of addon code, resulting in better performance in some cases as well as just plain looking better!

ISSUES REMAINING:
1. If a tamer is not on the list, it is not recognized.

FUTURE IMPROVEMENTS:
1. Solve issue 1 by scanning tooltips and using GameTooltip:SetUnit("target") GameTooltip:Show() *scan* GameTooltip:Hide().
2. Also include auto-accept and auto-complete for Battle Pet TRAINERS who give quests but don't battle).
3. Make auto tamer-battling optional instead of mandatory.
4. Add range detection for target switching functionality (may have to increase speed of OnUpdate to account for Nitro Boosts).
5. Intelligent detection of hat switching if initiated by user and not addon? (only needed in extremely rare cases probably - need to map out possible scenarios)
]]--

-- set addon name and namespace
local addonname, ASH = ...

-- localized functions and function returns to reduce run time for OnEvent triggers)
local _G = _G
local tonumber = _G.tonumber
local sub = _G.strsub
local ICL = _G.InCombatLockdown
local HFC = _G.HasFullControl
local UIWBP = _G.UnitIsWildBattlePet
local UE = _G.UnitExists
local UG = _G.UnitGUID
local IIB = _G.C_PetBattles.IsInBattle
local GII = _G.GetItemInfo
local GIII = _G.GetInventoryItemID
local HelmSlot = _G.GetInventorySlotInfo("HEADSLOT")
local TabardSlot = _G.GetInventorySlotInfo("TABARDSLOT")

-- used to store hat and tabard
local oldHat = false
local oldTabard = false
local realHat = false
local realTabard = false

-- used to track attempts
local attemptType = "N"
local updateAttempts = 0

local tamerlist = {}		-- list of pet tamers
local PetBattleType = false	-- variable to check what type of fight it is needed to implement user option choices)
local lastGUID = false		-- used to check after a pet battle whether the target changed during the pet battle
local ASH_Time = 2			-- used to store time for OnUpdate function starts at 2 so it will instantly try once)
local ASH_PBT = false		-- used to check if PetBattleTeams is detected and integration is supported

-- create event handling frame and register events
local ASH_Events = CreateFrame("FRAME", "AutoSafariHat_Handler")
ASH_Events:RegisterEvent("ADDON_LOADED")
ASH_Events:RegisterEvent("GOSSIP_SHOW")
ASH_Events:RegisterEvent("PET_BATTLE_CLOSE")
ASH_Events:RegisterEvent("PLAYER_CONTROL_LOST")
ASH_Events:RegisterEvent("PLAYER_LOGIN")
ASH_Events:RegisterEvent("PLAYER_REGEN_DISABLED")
ASH_Events:RegisterEvent("PLAYER_REGEN_ENABLED")
ASH_Events:RegisterEvent("PLAYER_TARGET_CHANGED")
ASH_Events:RegisterEvent("QUEST_COMPLETE")
ASH_Events:RegisterEvent("QUEST_DETAIL")
ASH_Events:RegisterEvent("QUEST_PROGRESS")


-- Special thanks to durandal42 for contributing this function (and therefore Pet Battle Teams support) to ASH!
local function ASH_EquipPetBattleTeam()
	if not AutoSafariHatOptions.AutoPetBattleTeams then
		return
	end
	
	local teamManager = ASH.LibStub("AceAddon-3.0"):GetAddon("PetBattleTeams"):GetModule("TeamManager")
	if not teamManager then
		return
	end

	local name = UnitName("target")
	if not name then
		return
	end
	
	-- get user's PBT options for empty pets and then override them
	local ignoreEmptyPets = teamManager:GetIgnoreEmptyPets()
	teamManager:SetIgnoreEmptyPets(true)

	local numTeams = teamManager:GetNumTeams()
	for teamId = 1, numTeams do
		if teamManager:TeamExists(teamId) then
			local teamName = teamManager:GetTeamName(teamId)
			
			if teamManager:GetTeamName(teamId) == name then
				-- inform the user unless they have told us not to
				if not AutoSafariHatOptions.APBTDisableText then
					print("Auto Safari Hat: Equipping team", teamName)
				end
				
				teamManager:SetSelected(teamId)
				
				return
			end
		end
	end
	
	-- return options of PBT to original state
	if not ignoreEmptyPets then
		teamManager:SetIgnoreEmptyPets(false)
	end
	
	-- inform the user unless they have told us not to
	if not AutoSafariHatOptions.APBTDisableText then
		print("Auto Safari Hat: No existing team named", name)
	end
end


-- check for delayed equipping code runs roughly every second while enabled)
local function ASH_OnUpdate(self, elapsed)
	-- update elapsed time occurs every frame)
	ASH_Time = ASH_Time + elapsed
	
	-- don't run more than once per second
	if ASH_Time > 1.0 then
		
		-- stop future tries if too many attempts
		if updateAttempts < 11 then
			
			-- if we're trying to equip
			if attemptType == "E" then
				
				-- if wearing normal hat
				if (AutoSafariHatOptions.Items == 1 or AutoSafariHatOptions.Items == 3) and GIII("player", HelmSlot) ~= 92738 then
					oldHat = _G.GetInventoryItemLink("player", HelmSlot)
					_G.EquipItemByName(92738)
					if oldHat and GII(oldHat) ~= GII(92738) then
						realHat = oldHat
					end
				end
				
				-- if wearing normal tabard
				if (AutoSafariHatOptions.Items == 2 or AutoSafariHatOptions.Items == 3) and GIII("player", TabardSlot) ~= 69209 and GIII("player", TabardSlot) ~= 69210 then
					oldTabard = _G.GetInventoryItemLink("player", TabardSlot)
					
					local tabardtype = 69209
					
					-- check which tabard you have
					for bag = 0, 4 do 
						for slot = 1, _G.GetContainerNumSlots(bag) do
							local itemID = _G.GetContainerItemID(bag, slot)
							if itemID == 69210 then
								tabardtype = 69210
								break
							end
						end
					end
					
					-- equip tabard
					_G.EquipItemByName(tabardtype)
					if oldTabard and GII(oldTabard) ~= GII(69209) and GII(oldTabard) ~= GII(69210) then
						realTabard = oldTabard
					end
				end
				
				-- advanced conditional if wearing safari hat and 1, if wearing guild tabard and 2, if wearing hat and tabard and 3, if 4)
				if (AutoSafariHatOptions.Items == 1 and GIII("player", HelmSlot) == 92738) or (AutoSafariHatOptions.Items == 2 and (GIII("player", TabardSlot) == 69209 or GIII("player", TabardSlot) == 69210)) or (AutoSafariHatOptions.Items == 3 and GIII("player", HelmSlot) == 92738 and (GIII("player", TabardSlot) == 69209 or GIII("player", TabardSlot) == 69210)) or (AutoSafariHatOptions.Items == 4) then
					-- equip was successful
					ASH_Events:SetScript("OnUpdate", nil)
					attemptType = "N"
					ASH_Time = 2
					updateAttempts = 0
				end
				
			-- if we're trying to unequip
			elseif attemptType == "U" then
				
				-- if wearing safari hat
				if (AutoSafariHatOptions.Items == 1 or AutoSafariHatOptions.Items == 3) and GIII("player", HelmSlot) == 92738 then
					_G.EquipItemByName(realHat)
				end
				
				-- if wearing guild tabard
				if (AutoSafariHatOptions.Items == 2 or AutoSafariHatOptions.Items == 3) and (GIII("player", TabardSlot) == 69209 or GIII("player", TabardSlot) == 69210) then
					_G.EquipItemByName(realTabard)
				end
				
				-- advanced conditional if not wearing safari hat and 1, if not wearing guild tabard and 2, if not wearing hat and tabard and 3, if 4)
				if (AutoSafariHatOptions.Items == 1 and GIII("player", HelmSlot) ~= 92738) or (AutoSafariHatOptions.Items == 2 and GIII("player", TabardSlot) ~= 69209 and GIII("player", TabardSlot) ~= 69210) or (AutoSafariHatOptions.Items == 3 and GIII("player", HelmSlot) ~= 92738 and GIII("player", TabardSlot) ~= 69209 and GIII("player", TabardSlot) ~= 69210) or (AutoSafariHatOptions.Items == 4) then
					-- unequip was successful
					ASH_Events:SetScript("OnUpdate", nil)
					attemptType = "N"
					ASH_Time = 2
					updateAttempts = 0
					oldHat = false
					oldTabard = false
				end
			end
		else
			-- we've tried many times, but have always failed
			ASH_Events:SetScript("OnUpdate", nil)
			attemptType = "N"
			ASH_Time = 2
			updateAttempts = 0
		end
		
		-- increment attempt counter and reset time to 0
		updateAttempts = updateAttempts + 1
		ASH_Time = 0
	end
end


local function ASH_EquipItem()
	
	-- Equip Safari Hat
	if (AutoSafariHatOptions.Items == 1 or AutoSafariHatOptions.Items == 3) and GIII("player", HelmSlot) ~= 92738 then
		oldHat = _G.GetInventoryItemLink("player", HelmSlot)
		_G.EquipItemByName(92738)
		if oldHat and GII(oldHat) ~= GII(92738) then
			realHat = oldHat
		end
	end
	
	-- Equip Guild Tabard
	if (AutoSafariHatOptions.Items == 2 or AutoSafariHatOptions.Items == 3) and GIII("player", TabardSlot) ~= 69209 and GIII("player", TabardSlot) ~= 69210 then
		oldTabard = _G.GetInventoryItemLink("player", TabardSlot)
		
		local tabardtype = 69209
		
		-- check which tabard you have
		for bag = 0, 4 do 
			for slot = 1, _G.GetContainerNumSlots(bag) do
				local itemID = _G.GetContainerItemID(bag, slot)
				if itemID == 69210 then
					tabardtype = 69210
					break
				end
			end
		end
		
		-- equip tabard
		_G.EquipItemByName(tabardtype)
		if oldTabard and GII(oldTabard) ~= GII(69209) and GII(oldTabard) ~= GII(69210) then
			realTabard = oldTabard
		end
	end
	
	-- start OnUpdate system to check if equipment is correct and fix if not
	if attemptType == "U" then
		-- attempt to Equip but use existing OnUpdate run
		attemptType = "E"
	elseif attemptType ~= "E" then
		-- attempt to Equip and start OnUpdate
		attemptType = "E"
		ASH_Events:SetScript("OnUpdate", ASH_OnUpdate)
	end
end


local function ASH_UnequipItem()
	-- Equip old Hat
	if (AutoSafariHatOptions.Items == 1 or AutoSafariHatOptions.Items == 3) and GIII("player", HelmSlot) == 92738 then
		_G.EquipItemByName(realHat)
	end
	
	-- Equip old Tabard
	if (AutoSafariHatOptions.Items == 2 or AutoSafariHatOptions.Items == 3) and (GIII("player", TabardSlot) == 69209 or GIII("player", TabardSlot) == 69210) then
		_G.EquipItemByName(realTabard)
	end
	
	-- start OnUpdate system to check if equipment is correct and fix if not)
	if attemptType == "E" then
		-- attempt to Unequip but use existing OnUpdate run
		attemptType = "U"
	elseif attemptType ~= "U" then
		-- attempt to Unequip and start OnUpdate
		attemptType = "U"
		ASH_Events:SetScript("OnUpdate", ASH_OnUpdate)
	end
	
	-- check if config option changed and erase tabard/hat if needed
	if AutoSafariHatOptions.Items ~= 1 and AutoSafariHatOptions.Items ~= 3 then
		oldHat = false
		realHat = false
	end
	if AutoSafariHatOptions.Items ~= 2 and AutoSafariHatOptions.Items ~= 3 then
		oldTabard = false
		realTabard = false
	end
end


local function ASH_OnEvent(self, event, ...)
	if event == "ADDON_LOADED" and ... == addonname then
		-- set options to defaults if not already found
		if not AutoSafariHatOptions then
			AutoSafariHatOptions = {}
			AutoSafariHatOptions.Items = 1
			AutoSafariHatOptions.WildPetHat = true
			AutoSafariHatOptions.TamerPetHat = true
			AutoSafariHatOptions.AutoPetBattleTeams = false
			AutoSafariHatOptions.APBTDisableText = false
			AutoSafariHatOptions.AutoQuestAccept = true
			AutoSafariHatOptions.AutoQuestComplete = true
			AutoSafariHatOptions.AttemptCombatFix = true
		end
		
		-- set new option to defaults if not already found
		if not AutoSafariHatOptions.Items then
			AutoSafariHatOptions.Items = 1
		end
		
		-- set option to see when user last changed options
		if not AutoSafariHatOptions.ManualChange then
			AutoSafariHatOptions.ManualChange = false
		end
		
		if not AutoSafariHatOptions.LastVersion then
			AutoSafariHatOptions.LastVersion = _G.GetAddOnMetadata(addonname, "Version")
		end
		
		-- unregister event since no further need to check addon loading
		ASH_Events:UnregisterEvent("ADDON_LOADED")
	
	elseif event == "PLAYER_LOGIN" then
		-- set up conditions for possible later integration with PetBattleTeams (i.e. if the user wants it)
		local _, _, _, enabled, loadable = _G.GetAddOnInfo("PetBattleTeams")
		
		if enabled or loadable then
			ASH_PBT = true
			
			-- since we only need LibStub to integrate with other addons that already use LibStub,
			-- we can rely on LibStub existing and hook into it without including it
			if _G["LibStub"] then
				-- access LibStub
				ASH.LibStub = _G["LibStub"]
			else
				ASH_PBT = false
			end
		end
	
	-- WildPetHat event
	elseif event == "PLAYER_TARGET_CHANGED" then
		if IIB() then
			return
		end
		
		if UIWBP("target") and not ICL() and HFC() and AutoSafariHatOptions.WildPetHat then
			PetBattleType = "wild"
			ASH_EquipPetBattleTeam()
			ASH_EquipItem()
		elseif attemptType == "E" or realHat or realTabard then
			if not ICL() and HFC() and AutoSafariHatOptions.WildPetHat then
				PetBattleType = false
				ASH_UnequipItem()
			end
		end
	
	-- TamerPetHat, AutoQuestAccept, and AutoQuestComplete events
	elseif event == "GOSSIP_SHOW" or event == "QUEST_DETAIL" or event == "QUEST_PROGRESS" or event == "QUEST_COMPLETE" then
		if UE("target") then
			local tamerID = tonumber(UG("target"):sub(6, 10), 16)
			
			if not tamerlist[tamerID] then
				return
			end
			
			if not _G.IsShiftKeyDown() then
				if event == "GOSSIP_SHOW" then
					-- check if any quests are completable
					local questindex
					if AutoSafariHatOptions.AutoQuestComplete and _G.GetGossipActiveQuests() then
						-- check if any quests are completable
						local questcomp
						for i = 1, _G.GetNumGossipActiveQuests() do
							questcomp = select(4 + ((i - 1) * 5), _G.GetGossipActiveQuests())
							if questcomp then
								questcompleted = true
								questindex = i
								break
							end
						end
					end
					
					-- first check available quests, then check if there are active quests AND we know it is completed (questindex), then lastly auto-battle
					if AutoSafariHatOptions.AutoQuestAccept and _G.GetGossipAvailableQuests() then
						_G.SelectGossipAvailableQuest(1)
						_G.CloseGossip()
					elseif AutoSafariHatOptions.AutoQuestAccept and _G.GetGossipActiveQuests() and questindex then
						_G.SelectGossipActiveQuest(questindex)
						_G.CloseGossip()
					elseif AutoSafariHatOptions.TamerPetHat and GetGossipOptions() then
						if not ICL() and HFC() then
							-- determine gossip battle option
							local gossipOptions = { _G.GetGossipOptions() }
							local battleOption
							for i = #gossipOptions, 1, -1 do
								if gossipOptions[i] and gossipOptions[i] ~= "gossip" then
									battleOption = i
									break
								end
							end
							
							-- select gossip battle option, equip items, start battle see improvement #3), set battle type, close gossip window
							if battleOption then
								_G.SelectGossipOption(1)
								PetBattleType = "tamer"
								ASH_EquipPetBattleTeam()
								ASH_EquipItem()
								StaticPopup1Button1:Click()
								_G.CloseGossip()
							end
						end
					end
				elseif AutoSafariHatOptions.AutoQuestAccept and event == "QUEST_DETAIL" then
					_G.AcceptQuest()
					_G.CloseGossip()
				elseif AutoSafariHatOptions.AutoQuestComplete and event == "QUEST_PROGRESS" and _G.IsQuestCompletable() then
					_G.CompleteQuest()
					_G.CloseGossip()
				elseif AutoSafariHatOptions.AutoQuestComplete and event == "QUEST_COMPLETE" and _G.GetNumQuestChoices() < 2 then
					_G.GetQuestReward(_G.GetNumQuestChoices())
					_G.CloseGossip()
				end
			end
		end
	
	elseif event == "PET_BATTLE_CLOSE" then
		if not ICL() and HFC() then
			if not UIWBP("target") and AutoSafariHatOptions.WildPetHat and ((AutoSafariHatOptions.WildPetHat and PetBattleType == "wild") or (AutoSafariHatOptions.TamerPetHat and PetBattleType == "tamer")) then
				PetBattleType = false
				ASH_UnequipItem()
			end
		end
		
	-- abandon attempts to equip or unequip if we enter combat
	elseif (event == "PLAYER_REGEN_DISABLED" or event == "PLAYER_CONTROL_LOST") and attemptType ~= "N" then
		ASH_Events:SetScript("OnUpdate", nil)
		attemptType = "N"
		ASH_Time = 0
		
	-- restart attempts to equip or unequip after leaving combat
	elseif AutoSafariHatOptions.AttemptCombatFix and event == "PLAYER_REGEN_ENABLED" then
		if not ICL() and HFC() and not IIB() then
			if UIWBP("target") then
				ASH_EquipPetBattleTeam()
				ASH_EquipItem()
				PetBattleType = "wild"
			else
				ASH_UnequipItem()
				PetBattleType = false
			end
		end
	end
end

-- set our event handler function
ASH_Events:SetScript("OnEvent", ASH_OnEvent)

-- create slash commands
SLASH_AUTOSAFARIHAT1 = "/autosafarihat"
SLASH_AUTOSAFARIHAT2 = "/ash"
SlashCmdList["AUTOSAFARIHAT"] = function(message)
	InterfaceOptionsFrame:Show()
	InterfaceOptionsFrameTab2:Click()
	
	local i = 1
	local currAddon = "InterfaceOptionsFrameAddOnsButton" .. i
	while _G[currAddon] do
		if _G[currAddon]:GetText() == "Auto Safari Hat" then
			_G[currAddon]:Click()
			break
		end
		i = i + 1
		currAddon = "InterfaceOptionsFrameAddOnsButton" .. i
	end
end


-- array of tamer NPC IDs defined down here for readability's sake
tamerlist[63194] = true
tamerlist[64330] = true
tamerlist[65648] = true
tamerlist[65651] = true
tamerlist[65655] = true
tamerlist[65656] = true
tamerlist[66126] = true
tamerlist[66135] = true
tamerlist[66136] = true
tamerlist[66137] = true
tamerlist[66352] = true
tamerlist[66372] = true
tamerlist[66412] = true
tamerlist[66422] = true
tamerlist[66436] = true
tamerlist[66442] = true
tamerlist[66452] = true
tamerlist[66466] = true
tamerlist[66478] = true
tamerlist[66512] = true
tamerlist[66515] = true
tamerlist[66518] = true
tamerlist[66520] = true
tamerlist[66522] = true
tamerlist[66550] = true
tamerlist[66551] = true
tamerlist[66552] = true
tamerlist[66553] = true
tamerlist[66557] = true
tamerlist[66635] = true
tamerlist[66636] = true
tamerlist[66638] = true
tamerlist[66639] = true
tamerlist[66675] = true
tamerlist[66730] = true
tamerlist[66733] = true
tamerlist[66734] = true
tamerlist[66738] = true
tamerlist[66739] = true
tamerlist[66741] = true
tamerlist[66815] = true
tamerlist[66819] = true
tamerlist[66822] = true
tamerlist[66824] = true
tamerlist[66918] = true
tamerlist[67370] = true
tamerlist[68462] = true
tamerlist[68463] = true
tamerlist[68464] = true
tamerlist[68465] = true
tamerlist[71924] = true
tamerlist[71926] = true
tamerlist[71927] = true
tamerlist[71929] = true
tamerlist[71930] = true
tamerlist[71931] = true
tamerlist[71932] = true
tamerlist[71933] = true
tamerlist[71934] = true